package com.xw.unification.handler;

import com.xw.unification.config.WebUnificationMvcProperties;
import com.xw.unification.config.WebUnificationMvcProperties.UnifiedExceptionProperties;
import com.xw.unification.constant.UnificationConstant;
import com.xw.unification.dto.ResultDTO;
import com.xw.unification.entity.ErrorBody;
import com.xw.unification.entity.UnifiedErrorBody;
import com.xw.unification.interfaces.UnifiedExceptionResolvable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.method.HandlerMethod;

import javax.servlet.http.HttpServletResponse;

public class UnifiedExceptionHandler {

    private static final ThreadLocal<HandlerMethod> THREAD_LOCAL_REAL_CONTROLLER_METHOD = new ThreadLocal<>();
    private static final Logger LOGGER = LoggerFactory.getLogger(UnifiedExceptionHandler.class);
    private final boolean unifiedResponseEnable;
    private final UnifiedExceptionProperties properties;

    public UnifiedExceptionHandler(WebUnificationMvcProperties webUnificationMvcProperties) {
        unifiedResponseEnable = webUnificationMvcProperties.getUnifiedResponse().isEnable();
        properties = webUnificationMvcProperties.getUnifiedException();
        LOGGER.info("[WebUnificationMvcUnifiedExceptionConfig] ### UnifiedExceptionHandler has been initialized");
    }

    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Object handleMethodNotSupportedException(Exception e) {
        return handleException(e,null);
    }

    @ExceptionHandler(Exception.class)
    public Object handleException(Exception e, HandlerMethod handlerMethod) {
        // 设置原始处理方法 ThreadLocal, 以便统一异常处理时使用
        THREAD_LOCAL_REAL_CONTROLLER_METHOD.remove();
        THREAD_LOCAL_REAL_CONTROLLER_METHOD.set(handlerMethod);
        if (e instanceof UnifiedExceptionResolvable) {
            return handleUnifiedException((UnifiedExceptionResolvable) e, handlerMethod);
        } else {
            return handleNativeException(e, handlerMethod);
        }
    }

    private Object handleNativeException(Exception e, HandlerMethod handlerMethod) {
        return doHandleException(e, handlerMethod);
    }

    private Object handleUnifiedException(UnifiedExceptionResolvable e, HandlerMethod handlerMethod) {
        if (!unifiedResponseEnable) {
            return doHandleException(e, handlerMethod);
        }
        if (e == null) {
            return null;
        }
        return ResultDTO.error(e);
    }

    private Object doHandleException(Object e, @SuppressWarnings("unused") HandlerMethod handlerMethod) {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (requestAttributes != null) {
            if (requestAttributes.getResponse() != null) {
                HttpServletResponse response = requestAttributes.getResponse();
                response.setStatus(UnificationConstant.DEFAULT_ERROR_STATUS.value());
            }
            if (e instanceof UnifiedExceptionResolvable) {
                if (((UnifiedExceptionResolvable) e).getBody() == null) {
                    return new ErrorBody(requestAttributes.getRequest(), (UnifiedExceptionResolvable) e);
                }
                return new UnifiedErrorBody(requestAttributes.getRequest(), (UnifiedExceptionResolvable) e);
            } else if (e instanceof Exception) {
                return new ErrorBody(requestAttributes.getRequest(), (Exception) e);
            }
        }
        return null;
    }

    protected static HandlerMethod getHandlerMethod() {
        HandlerMethod handlerMethod = THREAD_LOCAL_REAL_CONTROLLER_METHOD.get();
        THREAD_LOCAL_REAL_CONTROLLER_METHOD.remove();
        return handlerMethod;
    }
}
